package com.datagateway.component;

import com.datagateway.model.ProcessedData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.GZIPOutputStream;

/**
 * 数据备份管理器
 * 负责重要数据的备份和恢复，确保数据安全
 * 
 * @author Data Gateway Team
 * @version 1.0.0
 */
@Component
public class DataBackupManager {

    private static final Logger logger = LoggerFactory.getLogger(DataBackupManager.class);

    @Autowired
    private AlertManager alertManager;

    @Autowired
    private SystemMonitor systemMonitor;

    /**
     * 备份统计信息
     */
    private final AtomicLong totalBackups = new AtomicLong(0);
    private final AtomicLong totalRestores = new AtomicLong(0);
    private final AtomicLong totalBackupSize = new AtomicLong(0);
    private final AtomicLong totalRestoreSize = new AtomicLong(0);

    /**
     * 备份配置
     */
    private String backupBasePath = "./backups";
    private boolean backupEnabled = true;
    private int backupRetentionDays = 30;
    private int maxConcurrentBackups = 3;
    private boolean compressionEnabled = true;
    private String backupFormat = "json";

    /**
     * 备份执行器
     */
    private final ExecutorService backupExecutor = Executors.newFixedThreadPool(3, r -> {
        Thread t = new Thread(r, "backup-thread");
        t.setDaemon(true);
        return t;
    });

    /**
     * 备份任务队列
     */
    private final BlockingQueue<BackupTask> backupQueue = new LinkedBlockingQueue<>();

    /**
     * 备份任务状态
     */
    private final ConcurrentHashMap<String, BackupTask> backupTasks = new ConcurrentHashMap<>();

    /**
     * 启动备份管理器
     */
    public void start() {
        try {
            // 创建备份目录
            createBackupDirectory();
            
            // 启动备份处理线程
            startBackupProcessor();
            
            // 启动清理任务
            startCleanupTask();
            
            logger.info("数据备份管理器启动成功");
            
        } catch (Exception e) {
            logger.error("数据备份管理器启动失败", e);
            alertManager.sendSystemErrorAlert("数据备份管理器启动失败", e.getMessage());
        }
    }

    /**
     * 停止备份管理器
     */
    public void stop() {
        try {
            backupExecutor.shutdown();
            if (!backupExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
                backupExecutor.shutdownNow();
            }
            
            logger.info("数据备份管理器停止成功");
            
        } catch (Exception e) {
            logger.error("数据备份管理器停止失败", e);
        }
    }

    /**
     * 备份数据
     * 
     * @param data 要备份的数据
     * @param backupType 备份类型
     * @return 备份任务ID
     */
    public String backupData(ProcessedData data, BackupType backupType) {
        if (!backupEnabled) {
            logger.debug("数据备份已禁用，跳过备份: {}", data.getId());
            return null;
        }

        try {
            String taskId = generateTaskId();
            BackupTask task = new BackupTask(
                taskId,
                data,
                backupType,
                BackupStatus.PENDING,
                System.currentTimeMillis()
            );
            
            backupTasks.put(taskId, task);
            backupQueue.offer(task);
            
            logger.debug("数据备份任务已创建: {} -> {}", data.getId(), taskId);
            
            return taskId;
            
        } catch (Exception e) {
            logger.error("创建数据备份任务失败: {}", data.getId(), e);
            alertManager.sendSystemErrorAlert("数据备份任务创建失败", e.getMessage());
            return null;
        }
    }

    /**
     * 批量备份数据
     * 
     * @param dataList 要备份的数据列表
     * @param backupType 备份类型
     * @return 备份任务ID列表
     */
    public List<String> backupDataBatch(List<ProcessedData> dataList, BackupType backupType) {
        if (!backupEnabled) {
            logger.debug("数据备份已禁用，跳过批量备份: {} 条数据", dataList.size());
            return new ArrayList<>();
        }

        List<String> taskIds = new ArrayList<>();
        
        try {
            for (ProcessedData data : dataList) {
                String taskId = backupData(data, backupType);
                if (taskId != null) {
                    taskIds.add(taskId);
                }
            }
            
            logger.info("批量数据备份任务已创建: {} 个任务", taskIds.size());
            
        } catch (Exception e) {
            logger.error("批量数据备份任务创建失败", e);
            alertManager.sendSystemErrorAlert("批量数据备份任务创建失败", e.getMessage());
        }
        
        return taskIds;
    }

    /**
     * 恢复数据
     * 
     * @param backupId 备份ID
     * @return 恢复的数据
     */
    public ProcessedData restoreData(String backupId) {
        try {
            totalRestores.incrementAndGet();
            
            // 查找备份文件
            Path backupFile = findBackupFile(backupId);
            if (backupFile == null) {
                logger.warn("备份文件不存在: {}", backupId);
                return null;
            }
            
            // 读取备份文件
            ProcessedData data = readBackupFile(backupFile);
            if (data != null) {
                totalRestoreSize.addAndGet(data.getDataSize());
                logger.info("数据恢复成功: {} -> {} 字节", backupId, data.getDataSize());
            }
            
            return data;
            
        } catch (Exception e) {
            logger.error("数据恢复失败: {}", backupId, e);
            alertManager.sendSystemErrorAlert("数据恢复失败", e.getMessage());
            return null;
        }
    }

    /**
     * 获取备份任务状态
     * 
     * @param taskId 任务ID
     * @return 备份任务状态
     */
    public BackupTask getBackupTaskStatus(String taskId) {
        return backupTasks.get(taskId);
    }

    /**
     * 获取所有备份任务状态
     * 
     * @return 备份任务状态列表
     */
    public List<BackupTask> getAllBackupTaskStatus() {
        return new ArrayList<>(backupTasks.values());
    }

    /**
     * 删除备份
     * 
     * @param backupId 备份ID
     * @return 是否删除成功
     */
    public boolean deleteBackup(String backupId) {
        try {
            Path backupFile = findBackupFile(backupId);
            if (backupFile != null && Files.exists(backupFile)) {
                Files.delete(backupFile);
                logger.info("备份文件删除成功: {}", backupId);
                return true;
            }
            
            return false;
            
        } catch (Exception e) {
            logger.error("删除备份文件失败: {}", backupId, e);
            return false;
        }
    }

    /**
     * 清理过期备份
     */
    public void cleanupExpiredBackups() {
        try {
            Path backupDir = Paths.get(backupBasePath);
            if (!Files.exists(backupDir)) {
                return;
            }
            
            long cutoffTime = System.currentTimeMillis() - (backupRetentionDays * 24 * 60 * 60 * 1000);
            int deletedCount = 0;
            
            Files.walk(backupDir)
                .filter(Files::isRegularFile)
                .filter(path -> path.toString().endsWith(".backup"))
                .forEach(path -> {
                    try {
                        long fileTime = Files.getLastModifiedTime(path).toMillis();
                        if (fileTime < cutoffTime) {
                            Files.delete(path);
                            deletedCount++;
                        }
                    } catch (IOException e) {
                        logger.warn("删除过期备份文件失败: {}", path, e);
                    }
                });
            
            logger.info("清理过期备份文件: {} 个", deletedCount);
            
        } catch (Exception e) {
            logger.error("清理过期备份文件失败", e);
        }
    }

    /**
     * 获取备份统计信息
     * 
     * @return 备份统计信息
     */
    public BackupStatistics getStatistics() {
        return new BackupStatistics(
            totalBackups.get(),
            totalRestores.get(),
            totalBackupSize.get(),
            totalRestoreSize.get(),
            backupTasks.size(),
            backupEnabled,
            backupRetentionDays,
            maxConcurrentBackups,
            compressionEnabled,
            backupFormat,
            System.currentTimeMillis()
        );
    }

    /**
     * 设置备份配置
     * 
     * @param enabled 是否启用备份
     * @param retentionDays 保留天数
     * @param maxConcurrent 最大并发数
     * @param compression 是否启用压缩
     * @param format 备份格式
     */
    public void setBackupConfig(boolean enabled, int retentionDays, int maxConcurrent, 
                              boolean compression, String format) {
        this.backupEnabled = enabled;
        this.backupRetentionDays = retentionDays;
        this.maxConcurrentBackups = maxConcurrent;
        this.compressionEnabled = compression;
        this.backupFormat = format;
        
        logger.info("数据备份配置已更新: 启用={}, 保留天数={}, 最大并发={}, 压缩={}, 格式={}", 
                   enabled, retentionDays, maxConcurrent, compression, format);
    }

    /**
     * 创建备份目录
     */
    private void createBackupDirectory() throws IOException {
        Path backupDir = Paths.get(backupBasePath);
        if (!Files.exists(backupDir)) {
            Files.createDirectories(backupDir);
            logger.info("创建备份目录: {}", backupDir.toAbsolutePath());
        }
    }

    /**
     * 启动备份处理线程
     */
    private void startBackupProcessor() {
        for (int i = 0; i < maxConcurrentBackups; i++) {
            backupExecutor.submit(() -> {
                while (!Thread.currentThread().isInterrupted()) {
                    try {
                        BackupTask task = backupQueue.take();
                        processBackupTask(task);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        break;
                    } catch (Exception e) {
                        logger.error("备份任务处理失败", e);
                    }
                }
            });
        }
    }

    /**
     * 启动清理任务
     */
    private void startCleanupTask() {
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
            Thread t = new Thread(r, "backup-cleanup-thread");
            t.setDaemon(true);
            return t;
        });
        
        scheduler.scheduleAtFixedRate(this::cleanupExpiredBackups, 1, 1, TimeUnit.HOURS);
    }

    /**
     * 处理备份任务
     * 
     * @param task 备份任务
     */
    private void processBackupTask(BackupTask task) {
        try {
            task.setStatus(BackupStatus.RUNNING);
            task.setStartTime(System.currentTimeMillis());
            
            // 生成备份文件路径
            String fileName = generateBackupFileName(task);
            Path backupFile = Paths.get(backupBasePath, fileName);
            
            // 写入备份文件
            writeBackupFile(backupFile, task.getData());
            
            // 更新任务状态
            task.setStatus(BackupStatus.COMPLETED);
            task.setEndTime(System.currentTimeMillis());
            task.setBackupFile(backupFile.toString());
            task.setBackupSize(task.getData().getDataSize());
            
            // 更新统计信息
            totalBackups.incrementAndGet();
            totalBackupSize.addAndGet(task.getData().getDataSize());
            
            logger.info("数据备份完成: {} -> {} 字节", task.getData().getId(), task.getData().getDataSize());
            
        } catch (Exception e) {
            task.setStatus(BackupStatus.FAILED);
            task.setEndTime(System.currentTimeMillis());
            task.setErrorMessage(e.getMessage());
            
            logger.error("数据备份失败: {}", task.getData().getId(), e);
            alertManager.sendSystemErrorAlert("数据备份失败", e.getMessage());
        }
    }

    /**
     * 生成备份文件名
     * 
     * @param task 备份任务
     * @return 备份文件名
     */
    private String generateBackupFileName(BackupTask task) {
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
        String extension = compressionEnabled ? ".backup.gz" : ".backup";
        return String.format("%s_%s_%s%s", task.getBackupType().name(), task.getData().getId(), timestamp, extension);
    }

    /**
     * 写入备份文件
     * 
     * @param backupFile 备份文件路径
     * @param data 要备份的数据
     */
    private void writeBackupFile(Path backupFile, ProcessedData data) throws IOException {
        try (OutputStream outputStream = Files.newOutputStream(backupFile);
             OutputStream finalStream = compressionEnabled ? new GZIPOutputStream(outputStream) : outputStream;
             ObjectOutputStream objectStream = new ObjectOutputStream(finalStream)) {
            
            objectStream.writeObject(data);
            objectStream.flush();
        }
    }

    /**
     * 读取备份文件
     * 
     * @param backupFile 备份文件路径
     * @return 恢复的数据
     */
    private ProcessedData readBackupFile(Path backupFile) throws IOException, ClassNotFoundException {
        try (InputStream inputStream = Files.newInputStream(backupFile);
             InputStream finalStream = backupFile.toString().endsWith(".gz") ? 
                 new java.util.zip.GZIPInputStream(inputStream) : inputStream;
             ObjectInputStream objectStream = new ObjectInputStream(finalStream)) {
            
            return (ProcessedData) objectStream.readObject();
        }
    }

    /**
     * 查找备份文件
     * 
     * @param backupId 备份ID
     * @return 备份文件路径
     */
    private Path findBackupFile(String backupId) {
        try {
            Path backupDir = Paths.get(backupBasePath);
            if (!Files.exists(backupDir)) {
                return null;
            }
            
            return Files.walk(backupDir)
                .filter(Files::isRegularFile)
                .filter(path -> path.toString().contains(backupId))
                .findFirst()
                .orElse(null);
                
        } catch (Exception e) {
            logger.error("查找备份文件失败: {}", backupId, e);
            return null;
        }
    }

    /**
     * 生成任务ID
     * 
     * @return 任务ID
     */
    private String generateTaskId() {
        return "backup_" + System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8);
    }

    /**
     * 备份类型枚举
     */
    public enum BackupType {
        FULL,           // 完整备份
        INCREMENTAL,    // 增量备份
        DIFFERENTIAL,   // 差异备份
        MANUAL          // 手动备份
    }

    /**
     * 备份状态枚举
     */
    public enum BackupStatus {
        PENDING,        // 等待中
        RUNNING,        // 运行中
        COMPLETED,      // 已完成
        FAILED          // 失败
    }

    /**
     * 备份任务类
     */
    public static class BackupTask {
        private final String taskId;
        private final ProcessedData data;
        private final BackupType backupType;
        private BackupStatus status;
        private final long createTime;
        private long startTime;
        private long endTime;
        private String backupFile;
        private long backupSize;
        private String errorMessage;

        public BackupTask(String taskId, ProcessedData data, BackupType backupType, BackupStatus status, long createTime) {
            this.taskId = taskId;
            this.data = data;
            this.backupType = backupType;
            this.status = status;
            this.createTime = createTime;
        }

        // Getter和Setter方法
        public String getTaskId() { return taskId; }
        public ProcessedData getData() { return data; }
        public BackupType getBackupType() { return backupType; }
        public BackupStatus getStatus() { return status; }
        public void setStatus(BackupStatus status) { this.status = status; }
        public long getCreateTime() { return createTime; }
        public long getStartTime() { return startTime; }
        public void setStartTime(long startTime) { this.startTime = startTime; }
        public long getEndTime() { return endTime; }
        public void setEndTime(long endTime) { this.endTime = endTime; }
        public String getBackupFile() { return backupFile; }
        public void setBackupFile(String backupFile) { this.backupFile = backupFile; }
        public long getBackupSize() { return backupSize; }
        public void setBackupSize(long backupSize) { this.backupSize = backupSize; }
        public String getErrorMessage() { return errorMessage; }
        public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; }
    }

    /**
     * 备份统计信息类
     */
    public static class BackupStatistics {
        private final long totalBackups;
        private final long totalRestores;
        private final long totalBackupSize;
        private final long totalRestoreSize;
        private final int activeTasks;
        private final boolean backupEnabled;
        private final int retentionDays;
        private final int maxConcurrent;
        private final boolean compressionEnabled;
        private final String backupFormat;
        private final long timestamp;

        public BackupStatistics(long totalBackups, long totalRestores, long totalBackupSize, long totalRestoreSize,
                              int activeTasks, boolean backupEnabled, int retentionDays, int maxConcurrent,
                              boolean compressionEnabled, String backupFormat, long timestamp) {
            this.totalBackups = totalBackups;
            this.totalRestores = totalRestores;
            this.totalBackupSize = totalBackupSize;
            this.totalRestoreSize = totalRestoreSize;
            this.activeTasks = activeTasks;
            this.backupEnabled = backupEnabled;
            this.retentionDays = retentionDays;
            this.maxConcurrent = maxConcurrent;
            this.compressionEnabled = compressionEnabled;
            this.backupFormat = backupFormat;
            this.timestamp = timestamp;
        }

        // Getter方法
        public long getTotalBackups() { return totalBackups; }
        public long getTotalRestores() { return totalRestores; }
        public long getTotalBackupSize() { return totalBackupSize; }
        public long getTotalRestoreSize() { return totalRestoreSize; }
        public int getActiveTasks() { return activeTasks; }
        public boolean isBackupEnabled() { return backupEnabled; }
        public int getRetentionDays() { return retentionDays; }
        public int getMaxConcurrent() { return maxConcurrent; }
        public boolean isCompressionEnabled() { return compressionEnabled; }
        public String getBackupFormat() { return backupFormat; }
        public long getTimestamp() { return timestamp; }
    }
}
